package cpu

import (
	"risc-vm/common"
	error2 "risc-vm/vm/error"
	"risc-vm/vm/rv32/mem"
	"time"
)

type CPU struct {
	memory *mem.Memory
	// PCR 寄存器一共4096个位置，但有很多位置是留给未来扩展用的。目前未用到扩展，所用到的最大寄存器编号为3074(0xC02)
	PCR [3075]uint64
	// 通用寄存器x0~x31
	XReg      [32]uint64
	PC        uint32
	Trap      *error2.RISCVTrap
	TLB       [524288]uint32
	ITLB      [4]uint32
	ITLBstuff [4]uint32
}

func New(m *mem.Memory) *CPU {
	return new(CPU).Init(m)
}

func (c *CPU) Init(m *mem.Memory) *CPU {
	c.memory = m
	c.Trap = nil
	// PC, defaults to 0x2000 according to the ISA, documented in
	// processor.cc
	// Even in RV64, this must remain as a Number (not a Long) because
	// of Array indexing requirements.
	// Possibly improve on this later by nesting arrays, but trying to address/
	// store more than ~4GiB is probably not a good idea anyways
	c.PC = 0x2000
	// 初始化状态寄存器
	c.PCR[CSR_STATUS.Index] = statusRegInit()
	// [todo] - Detect when this is the proxy kernel
	// FOR NOW, ASSUME THAT THIS IS THE PROXY KERNEL
	// c.StoreWordToMem(0x0, uint32(1)) // single core
	c.StoreWordToMem(0x0, uint32(m.GetMemAmt()))

	// 初始化系统时钟
	c.ResetWallClock()

	return c
}

func (c *CPU) StoreWordToMem(addr uint32, val uint32) {
	if c.IsVMOn() {

	} else {

	}
}

func (c *CPU) IsVMOn() bool {
	return (c.PCR[CSR_STATUS.Index] & SR_VM) != 0x0
}

func (c *CPU) ResetWallClock() {
	now := time.Now().UnixMilli()
	c.PCR[CSR_TIME.Index] = uint64(now)
}

func (c *CPU) StoreDoubleToMem(addr uint32, uint642 uint64) {
	if c.IsVMOn() {

	} else {

	}
}

// 地址转换
func (c *CPU) translate(addr common.Long, accessType int) uint32 {
	origaddr := addr.GetLow32Uint()
	origaddrVPN := origaddr >> 13
	pte := c.TLB[origaddrVPN]
	if pte == 0 {

	}
	return 0
}

// does the page table walk only - no permission checks here
func (c *CPU) walk(vaddr common.Long) *common.Long {
	//ptbr := c.PCR[CSR_PTBR.Index]
	//
	//// main walk for loop
	//idx = vaddr.
	return nil
}

func (c *CPU) loadInstFromMem(addr uint32) uint32 {
	return c.memory.Read(addr >> 2)
}

func (c *CPU) Run() {
	inst := c.loadInstFromMem(c.PC)
	c.runInstruction(inst)
}
